import type { ComponentPropsWithoutRef } from 'react';
import { useEffect, useRef } from 'react';

import clsx from 'clsx';
import { animate, useInView, useMotionValue, useSpring } from 'motion/react';

import type { AnimationPlaybackControls } from 'motion/react';

/**
 * NumberTicker 组件的属性接口
 *
 * @interface NumberTickerProps
 * @extends ComponentPropsWithoutRef<'span'>
 *
 * @property {number} [decimalPlaces=0] - 小数位数，默认为 0
 * @property {number} [delay=0] - 延时时间（单位：秒），默认为 0
 * @property {'down' | 'up'} [direction='up'] - 动画方向，默认为 'up'
 * @property {number} [duration] - 动画时长（单位：秒），如果不指定则使用 spring 动画
 * @property {string} [prefix=''] - 前缀，默认为空字符串
 * @property {number} [startValue=0] - 起始值，默认为 0
 * @property {string} [suffix=''] - 后缀，默认为空字符串
 * @property {number} value - 目标值
 */
interface NumberTickerProps extends ComponentPropsWithoutRef<'span'> {
  decimalPlaces?: number;
  delay?: number;
  direction?: 'down' | 'up';
  duration?: number;
  prefix?: string;
  startValue?: number;
  suffix?: string;
  value?: number;
}

/**
 * NumberTicker 组件
 *
 * @param {NumberTickerProps} props - 组件属性
 * @returns {JSX.Element} - 渲染的 JSX 元素
 *
 * @example
 * // 示例：向上滚动到目标值
 * <NumberTicker value={1000} prefix="¥" suffix="元" />
 *
 * // 示例：向下滚动到目标值
 * <NumberTicker value={500} direction="down" />
 */
const NumberTicker = ({
  className,
  decimalPlaces = 0,
  delay = 0,
  direction = 'up',
  duration,
  prefix = '',
  startValue = 0,
  suffix = '',
  value = 0,
  ...props
}: NumberTickerProps) => {
  // 创建对 span 元素的引用
  const ref = useRef<HTMLSpanElement>(null);

  // 根据方向设置结束值
  const endValue = direction === 'down' ? 0 : value || 0;

  // 使用 motion/react 提供的 useMotionValue 创建动画值
  const motionValue = useMotionValue(direction === 'down' ? value : 0);

  // 使用 useInView 检测元素是否进入视口
  const isInView = useInView(ref, { margin: '0px', once: true });

  // 使用 useSpring 创建弹簧动画效果
  const springValue = useSpring(motionValue, {
    damping: 60,
    stiffness: 100,
  });

  /**
   * 更新文本内容
   *
   * @param {number} latest - 最新的动画值
   */
  function updateTextContent(latest: number) {
    if (!ref.current) return;

    // 格式化数字
    const formattedNumber = Intl.NumberFormat('en-US', {
      maximumFractionDigits: decimalPlaces,
      minimumFractionDigits: decimalPlaces,
    }).format(Number(latest.toFixed(decimalPlaces)));

    // 更新 DOM 文本内容
    ref.current.textContent = `${prefix} ${formattedNumber} ${suffix}`;
  }

  // 监听 isInView 状态，当元素进入视口时启动动画
  useEffect(() => {
    if (isInView) {
      setTimeout(() => {
        motionValue.set(endValue);
      }, delay * 1000);
    }
  }, [motionValue, isInView, delay, endValue]);

  // 根据 duration 是否存在选择不同的动画方式
  useEffect(() => {
    let animation: AnimationPlaybackControls | undefined;

    if (duration) {
      // 使用 animate 方法创建基于时间的动画
      animation = animate(
        motionValue.get(), // 起始值
        endValue, // 目标值
        {
          duration, // 动画时长（秒）
          onUpdate: latest => {
            motionValue.set(latest); // 更新 motionValue
            updateTextContent(latest); // 更新文本内容
          },
        }
      );
    } else {
      // 使用弹簧动画
      springValue.on('change', latest => {
        updateTextContent(latest);
      });
    }

    // 清理函数，取消动画
    return () => animation?.cancel();
  }, [motionValue, springValue, endValue, duration]);

  // 渲染组件
  return (
    <span
      className={clsx('inline-block tabular-nums tracking-wider ', className)}
      ref={ref}
      {...props}
    >
      {prefix} {startValue} {suffix}
    </span>
  );
};

export default NumberTicker;
